﻿using AppDesign.Models.ManageType;
using Idte.Core.Infrastructure.Repository;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;

namespace AppDesign.Models
{
    public class AppDesignContext : DbContext
    {
        public AppDesignContext()
            : base("AppDesignDb")
        {
            Database.SetInitializer(new AppDesignDatabaseInitializer());
        }

        public void AccessAttribute(Type t, Assembly assembly, DbModelBuilder modelBuilder)
        {
            var list = t.GetProperties();
            foreach(var l in list){
                var InCustomAttr = (DisableCascade)l.GetCustomAttribute(typeof(DisableCascade), true);
                if (InCustomAttr != null)
                {
                    var propName = l.Name;
                    var childTypename = l.PropertyType.GenericTypeArguments[0].FullName;
                    Type childType = assembly.GetType(childTypename);
                    var foreignKeyname = InCustomAttr.ReferForeignKey;
                    var fkType = childType.GetProperty(foreignKeyname); // Type of foreign key
                    var foreignObjectname = InCustomAttr.ReferObj;

                    _ConfigCascade(modelBuilder, assembly, t, childType, propName, foreignObjectname, foreignKeyname, fkType.PropertyType);

                }
            }
        }

        public void _ConfigCascade(DbModelBuilder modelBuilder, Assembly assembly, Type parentType, Type childType, string Propname, string ForeignObj, string ForeignKey, Type fkType)
        {
            #region Call 'Entity' method
            var _config = modelBuilder.GetType().GetMethod("Entity")
                .MakeGenericMethod(parentType)
                .Invoke(modelBuilder, null);

            #endregion
            #region Call 'HasMany' method
            var hasMany = _config.GetType().GetMethod("HasMany");
            var paramEx1 = Expression.Parameter(parentType, "t");
            var lambdaEx1 = Expression.Lambda(Expression.Property(paramEx1, Propname), paramEx1);
            var _config2 = hasMany.MakeGenericMethod(childType).Invoke(_config, new object[] { lambdaEx1 });
            #endregion
            #region Call 'WithRequired' method which need parametter.
            // This is not GENERIC method (???)
            MethodInfo withRequired = null;
            var listme = _config2.GetType().GetMethods();
            foreach (var method in listme)
            {
                if (method.Name == "WithRequired" && method.GetParameters().Length > 0)
                {
                    //Call 'Myparam.ParameterType' if you want to specify type of parametter
                    withRequired = method;
                    break;
                }
            }
            var paramEx2 = Expression.Parameter(childType, "t");
            var lambdaEx2 = Expression.Lambda(Expression.Property(paramEx2, ForeignObj), paramEx2);
            var _config3 = withRequired.Invoke(_config2, new object[] { lambdaEx2 });
            #endregion
            #region Call 'HasForeignKey' method
            var HasForeignKey = _config3.GetType().GetMethod("HasForeignKey");
            var paramEx3 = Expression.Parameter(childType, "g");
            var lambdaEx3 = Expression.Lambda(Expression.Property(paramEx3, ForeignKey), paramEx3);
            var _config4 = HasForeignKey.MakeGenericMethod(fkType).Invoke(_config3, new object[] { lambdaEx3 });
            #endregion
            #region Call 'WillCascadeOnDelete' method
            MethodInfo WillCascadeOnDelete = null;
            var listmethod = _config4.GetType().GetMethods();
            foreach (var method in listmethod)
            {
                if (method.Name == "WillCascadeOnDelete" && method.GetParameters().Length > 0)
                {
                    WillCascadeOnDelete = method;
                    break;
                }
            }

            WillCascadeOnDelete.Invoke(_config4, new object[] { false });
            #endregion
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
 
                var entityTypes = assembly
                  .GetTypes()
                  .Where(t =>
                    t.GetCustomAttributes(typeof(AggregateAttribute), inherit: false)
                    .Any());
                foreach (var type in entityTypes)
                {
                    entityMethod.MakeGenericMethod(type)
                      .Invoke(modelBuilder, new object[] { });

                    AccessAttribute(type, assembly, modelBuilder);
    
                }
            }

        }
    }
}